import { Service } from 'egg';
import * as moment from 'moment';
import {OrderedSchedule, OrderedScheduleStatus} from '../model/ordered_schedule';
import {ReqUpdateOrder, UpdateStatus, UpdateType} from '../model/req_update_order';

export default class ReqUpdateOrderService extends Service {

    public async getNeedConfirmReqUpdateOrder(teacherId: number) {
        const { service } = this;
        return await service.db.reqUpdateOrder.findNeedConfirmByTeacherId(teacherId);
    }

    /**
     * 检查当前课程是否可以推迟
     */
    public async checkPutOff(schedule: OrderedSchedule): Promise<OrderedSchedule[]> {
        // 找到满足当前排课
        const { service, ctx } = this;
        const {
            date,
            time_from,
            time_to,
            student_id,
            teacher_id,
        } = schedule;
        let schedules = await service.db.orderedSchedule.getSameClassByStudent(date, time_from, time_to, student_id);
        if (!schedules || schedules.length === 0) {
            ctx.helper.invalidateParamsError();
        }
        schedules = schedules.filter((s) => this.isSameClass(s, schedule));
        schedules.forEach((s) => {
            s.date = moment(s.date, 'YYYY-MM-DD').add(7, 'days').format('YYYY-MM-DD');
            s.status = OrderedScheduleStatus.Normal;
        });
        const isConflicted = await service.orderedSchedule.checkConflict(teacher_id, schedules, false);
        if (isConflicted) {
            this.ctx.helper.invalidateParamsError('排课冲突');
        }
        return schedules;
    }

    public async updateOrderedOrder(reqUpdateOrder: ReqUpdateOrder) {
        const { service } = this;
        const conn = await this.app.mysql.beginTransaction(); // 初始化事务
        try {
            // 保存更改日期请求
            await service.db.reqUpdateOrder.saveOne(reqUpdateOrder, conn);
            await service.db.orderedSchedule.update({
                    id: reqUpdateOrder.ordered_schedule_id,
                    status: OrderedScheduleStatus.UpdateNeedConfirm,
                }, conn);
            conn.commit();
        } catch (err) {
            conn.rollback();
        }
        // 发送短信通知
        if (UpdateType.ChangeAll === reqUpdateOrder.type ||
            UpdateType.ChangeOnce === reqUpdateOrder.type) {
            service.sms.sendReqUpdateOrder(reqUpdateOrder);
        } else {
            service.sms.sendReqPutoffOrder(reqUpdateOrder);
        }
    }

    public async cancelReqUpdateOrder(id: number, teacherId: number) {
        const { service } = this;
        const reqUpdateOrder = await service.db.reqUpdateOrder.findOneById(id);
        if (!reqUpdateOrder) {
            this.ctx.helper.notFound();
        }
        const orderedSchedule =
            await service.db.orderedSchedule.findOneById(reqUpdateOrder!.ordered_schedule_id);
        if (!orderedSchedule) {
            this.ctx.helper.notFound();
        }
        if (orderedSchedule!.teacher_id !== teacherId) {
            // 并非当前老师的排课，确认无效
            this.ctx.helper.notFound();
        }
        const mysql = this.app.mysql;
        const result = await mysql.beginTransactionScope(async (conn) => {
            // don't commit or rollback by yourself
            // 取消请求
            await service.db.reqUpdateOrder.cancelReq(id, conn);
            await service.db.orderedSchedule.update({
                id: reqUpdateOrder!.ordered_schedule_id,
                status: OrderedScheduleStatus.Rejected,
            }, conn);
            // 查询当前当前学生对应的管理员，发出消息
            const adminId = await service.db.user.findAdminId(orderedSchedule!.student_id);
            const teacherUser = await service.db.user.findOneById(orderedSchedule!.teacher_id);
            const studentUser = await service.db.user.findOneById(orderedSchedule!.student_id);
            if (adminId) {
                const teacherName = teacherUser ? teacherUser.username : '';
                const studentName = studentUser ? studentUser.username : '';
                await service.db.msg.create(adminId, `${teacherName}老师拒绝了${studentName}同学的调课申请`);
            }
        }, this.ctx);
        this.app.logger.info('req-update-order-reject ' + JSON.stringify(result));
        return { success: true };
    }

    public async confirmReqUpdateOrder(id: number, teacherId: number) {
        const { service } = this;
        // 通过ID查找请求
        const reqUpdateOrder = await service.db.reqUpdateOrder.findOneById(id);
        if (!reqUpdateOrder || reqUpdateOrder.status !== UpdateStatus.NeedConfirm) {
            // ID不存在，或者状态不是未确认
            this.ctx.helper.notFound();
        }
        // 查找对应的排课
        const orderedSchedule = await service.db.orderedSchedule.findOneById(reqUpdateOrder!.ordered_schedule_id);
        if (!orderedSchedule || orderedSchedule.status !== OrderedScheduleStatus.UpdateNeedConfirm) {
            // 排课信息不存在或者排课状态不正确
            this.ctx.helper.notFound('排课信息不存在或者不需要确认');
        }
        if (orderedSchedule!.teacher_id !== teacherId) {
            // 并非当前老师的排课，确认无效
            this.ctx.helper.unauth('并非当前老师的排课');
        }
        await this.handleReqUpdateOrder(reqUpdateOrder!, orderedSchedule!);
    }

    private async handleReqUpdateOrder(reqUpdateOrder: ReqUpdateOrder, orderedSchedule: OrderedSchedule) {
        const conn = await this.app.mysql.beginTransaction(); // 初始化事务
        const { teacher_id, student_id} = orderedSchedule;
        try {
            const { service } = this;
            switch (reqUpdateOrder.type) {
                case UpdateType.ChangeOnce:
                    await this.changeOrderSchedule(teacher_id, student_id, reqUpdateOrder, conn);
                    service.sms.sendConfirmUpdateOrder(reqUpdateOrder);
                    break;
                case UpdateType.ChangeAll:
                    await this.changeAllOrderSchedules(reqUpdateOrder, orderedSchedule, conn);
                    service.sms.sendConfirmUpdateOrder(reqUpdateOrder);
                    break;
                case UpdateType.CancelOnce:
                    await service.sms.sendConfirmPutoffOrder(reqUpdateOrder);
                    await service.db.orderedSchedule.removeOneById(orderedSchedule.id, conn);
                    break;
                case UpdateType.CancelAll:
                    await this.removeAllOrderedSchedules(orderedSchedule, conn);
                    // 取消所有可能不需要发送短信
                    break;
                case UpdateType.PutOff:
                    await service.sms.sendConfirmPutoffOrder(reqUpdateOrder);
                    await this.putOff(orderedSchedule, conn);
                    break;
                default:
                    break;
            }
            // 更新状态为已请求
            await service.db.reqUpdateOrder.finishReq(reqUpdateOrder.id!, conn);
            await conn.commit(); // 提交事务
        } catch (err) {
            // error, rollback
            await conn.rollback(); // 一定记得捕获异常后回滚事务！！
            throw err;
        }
    }

    private async removeAllOrderedSchedules(orderedSchedule: OrderedSchedule, conn: any) {
        const dbOrderedSchedule = this.service.db.orderedSchedule;
        const {
            date,
            time_from,
            time_to,
            student_id,
        } = orderedSchedule;
        const schedules = await dbOrderedSchedule.getSameClassByStudent(date, time_from, time_to, student_id, conn);
        for (const schedule of schedules) {
            // 判断是否是同一节课
            if (this.isSameClass(orderedSchedule, schedule)) {
                await dbOrderedSchedule.removeOneById(schedule.id, conn);
            }
        }
    }

    /**
     * 修改后续所有排课
     * @param {ReqUpdateOrder} reqUpdateOrder
     * @param {OrderedSchedule} orderedSchedule
     * @param conn
     * @returns {Promise<void>}
     */
    private async changeAllOrderSchedules(reqUpdateOrder: ReqUpdateOrder, orderedSchedule: OrderedSchedule, conn: any) {
        const dbOrderedSchedule = this.service.db.orderedSchedule;
        const {
            date,
            time_from,
            time_to,
            student_id,
        } = orderedSchedule;
        const d1: any = moment(date);
        const d2: any = moment(reqUpdateOrder.date);
        const dateDelta = moment.duration(d2 - d1, 'ms').days();
        const schedules = await dbOrderedSchedule.getSameClassByStudent(date, time_from, time_to, student_id, conn);
        for (const schedule of schedules) {
            // 判断是否是同一节课
            if (this.isSameClass(orderedSchedule, schedule)) {
                schedule.date = moment(schedule.date).add(dateDelta, 'days').format('YYYY-MM-DD');
                await this.changeOrderSchedule(orderedSchedule.teacher_id,
                    orderedSchedule.student_id, reqUpdateOrder, conn);
            }
        }
    }

    /**
     * 修改一次排课
     * @param {number} teacherId
     * @param {number} studentId
     * @param {ReqUpdateOrder} reqUpdateOrder
     * @param conn
     * @returns {Promise<void>}
     */
    private async changeOrderSchedule(teacherId: number, studentId: number, reqUpdateOrder: ReqUpdateOrder, conn: any) {
        const { service } = this;
        if (moment().isAfter(moment(reqUpdateOrder.date))) {
            this.ctx.helper.invalidateParamsError('修改时间非法');
        }
        const isConflicted = await service.orderedSchedule.checkConflict(teacherId, [{
            date: reqUpdateOrder.date,
            time_from: reqUpdateOrder.time_from,
            time_to: reqUpdateOrder.time_to,
            teacher_id: teacherId,
            student_id: studentId,
            id: reqUpdateOrder.ordered_schedule_id,
        }], true);
        if (isConflicted) {
            this.ctx.helper.invalidateParamsError('排课冲突');
        }
        await service.db.orderedSchedule.update({
            id: reqUpdateOrder.ordered_schedule_id,
            date: reqUpdateOrder.date,
            time_from: reqUpdateOrder.time_from,
            time_to: reqUpdateOrder.time_to,
            status: OrderedScheduleStatus.Normal,
        }, conn);
    }

    private async putOff(schedule: OrderedSchedule, conn: any) {
        // 找到满足当前排课
        const schedules = await this.checkPutOff(schedule);
        // 判断这些时间是否在老师的合法时间内
        await this.service.orderedSchedule.updateAll(schedules, conn);
    }

    /**
     * 判断两节课是否是每个星期的同一节课
     * @param {OrderedSchedule} schedule1
     * @param {OrderedSchedule} schedule2
     * @returns {boolean}
     */
    private isSameClass(schedule1: OrderedSchedule, schedule2: OrderedSchedule): boolean {
        // 转换成星期
        const day1 = this.getDay(schedule1.date); // 星期
        const day2 = this.getDay(schedule2.date); // 星期
        const timeFrom1 = moment(schedule1.time_from, 'HH:mm:SS').format('HH:mm');
        const timeTo1 = moment(schedule1.time_to, 'HH:mm:SS').format('HH:mm');
        const timeFrom2 = moment(schedule2.time_from, 'HH:mm:SS').format('HH:mm');
        const timeTo2 = moment(schedule2.time_to, 'HH:mm:SS').format('HH:mm');
        return day1 === day2
            && timeFrom1 === timeFrom2
            && timeTo1 === timeTo2;
    }

    private getDay(date: string): number {
        return moment(date, 'YYYY-MM-DD').day(); // 星期
    }
}
